class RelationBase {
  constructor(field_info, payload = {}) {
    const { env, context = {} } = payload
    this.field_info = field_info
    this._env = env
    this._context = context
    this.Model = env.model(field_info.relation, {
      metadata: field_info.metadata
    })
  }

  get env() {
    return this._env
  }

  get context() {
    return this._context
  }
}

export class Relation extends RelationBase {
  constructor(field_info, payload = {}) {
    super(field_info, payload)
  }

  async load_select_options(kwargs = {}) {
    const { record = {}, ...kwargs2 } = kwargs
    const domain_get = () => {
      const { domain, async_domain } = this.field_info

      if (async_domain && typeof async_domain === 'function') {
        return async_domain({ record, env: this.env })
      } else {
        return domain
      }
    }
    const domain = await domain_get()

    const { args = [], limit = 8, ...kws } = kwargs2

    return this.Model.web2_name_search({
      args: [...args, ...domain],
      limit,
      ...kws
    })
  }
}

export class Many2many extends RelationBase {
  constructor(field_info, payload = {}) {
    super(field_info, payload)
  }

  // _merge_values(values = []) {
  //   // todo, to chaek. no used, to del
  //   // call by ojsmodel.ojs_onchange
  //   // console.log('_merge_values, m2m', values)
  //   const records2 = values.reduce((acc, tup) => {
  //     const [op, rid, vals] = tup
  //     if (op === 5) {
  //       acc = {}
  //     } else if (op === 4) {
  //       acc[rid] = { id: rid }
  //     } else {
  //       console.log('_merge_values, m2m', values)
  //       //
  //     }

  //     return acc
  //   }, {})

  //   return Object.values(records2)
  // }
}

export class One2many extends RelationBase {
  constructor(field_info, payload = {}) {
    super(field_info, payload)

    // 处理 递归数据, 用到其中的 merge 函数
    this.Editmodel = this.env.editx2m(field_info.relation, {
      // metadata: field_info.metadata,
      field_info,
      context: this.context
    })

    this.datastore = {
      records: [],
      values: []
    }
  }

  get records_display() {
    const records = this._merge_data(
      this.datastore.records,
      this.datastore.values
    )
    const records2 = this._merge_to_display(records)
    return records2
  }

  get records_edit() {
    const records = this._merge_data(
      this.datastore.records,
      this.datastore.values
    )

    // console.log('records_edit ok', records, this.datastore)

    return records
  }

  set_edit({ records = [], values = [] }) {
    this.datastore.records = [...records]
    this.datastore.values = [...values]

    return this.records_display
  }

  _split_for_write(values = []) {
    return values.filter(item => {
      return [2, 1, 0].includes(item[0])
    })
  }

  _merge_values(values = []) {
    // call by Model.onchange after,
    // call by this.upinsert_one1
    // console.log('_merge_values, o2m', values)

    const records2 = values.reduce((acc, tup) => {
      const [op, rid_todo, vals] = tup

      // 页面 编辑 经过 了 upinsert_one1. [0, rid, {}],  rid 已经生成
      // onchange 返回 未经过 upinsert_one1. [0, false, {}],  rid 未生成
      // 因此需要 处理下  rid2

      const rid = op === 0 ? rid_todo || virtualHelper.virtual_id() : rid_todo

      if (op === 5) {
        acc = {}
      } else {
        // console.log('_merge_values, o2m', values)
        const oldVal = acc[rid]
        if (!oldVal) {
          acc[rid] = [op, rid, vals]
        } else {
          const [op2, rid2, vals2] = oldVal

          if (op2 === 2) {
            // 先删除, 再编辑. 此情况不可能出现.
          } else if (op2 === 1 && op === 2) {
            // 先编辑, 再删除. 需要 更新为删除
            acc[rid] = tup
          } else if (op2 === 0 && op === 2) {
            // 先新增, 再删除. 需要 将已有的新增 抵消掉.
            delete acc[rid]
          } else if (op2 === 1 || op2 === 0) {
            // 先编辑, 再(编辑, 只能出现编辑). 或
            // 先新增, 再(新增, 对新增行的编辑, )
            // 需要 merge 数据. 是否有递归. todo
            // console.log('todo', vals2, vals)
            const vals_done = this.Editmodel._merge_values(vals2, vals)
            acc[rid] = [op2, rid2, vals_done]
          } else {
            // 此情况不存在. do nothing
          }
        }
        //
      }

      return acc
    }, {})

    // console.log('_merge_values, o2m2', records2)

    return Object.values(records2)
  }

  _merge_data(records = [], values = []) {
    // call by Editmodel._merge_data
    // [ op, id, {vals}]
    // op=4: 表示只读.  vals 中数据格式为只读格式
    // op=2,1,0: 表示编辑.  vals 中数据格式为编辑格式
    //
    const records2 = records.map(item => [4, item.id, item])
    return [...records2, ...values]
  }

  _merge_to_modifiers(records = []) {
    // call by Editmodel._merge_to_modifiers

    const records2 = records.reduce((acc, tup) => {
      const [op, rid, vals] = tup
      if (op === 5) {
        acc = {}
      } else if ([3, 2].includes(op)) {
        delete acc[rid]
      } else if ([4].includes(op)) {
        acc[rid] = rid
      } else if ([1].includes(op)) {
        acc[rid] = rid
      } else if ([0].includes(op)) {
        acc[rid] = rid
      } else {
        //
      }

      return acc
    }, {})

    return Object.values(records2)
  }

  _merge_to_display(records = []) {
    // call by Editmodel._merge_to_display
    // 将标准格式转换为 显示格式供 前端页面直接显示用
    // 标准格式为  tuples
    // 其中一个 tup 为 [ op, id, {vals}]
    // op=4: 表示只读.  vals 中数据格式为只读格式
    // op=2,1,0: 表示编辑.  vals 中数据格式为编辑格式
    // merge 为 display 格式 为 [{}]
    // 1. id不同分别处理
    // 2. op=4: 直接保留原样
    // 3. op=2: 直接删除
    // 4. op=1: 与 op=4的数据 merge.
    // 5. op=0: 直接保留原样
    // 6. op=1,0: 需要递归处理
    // console.log('_merge_to_display', records)

    const records2 = records.reduce((acc, tup) => {
      const [op, rid, vals] = tup
      if (op === 5) {
        acc = {}
      } else if ([3, 2].includes(op)) {
        delete acc[rid]
      } else if ([4].includes(op)) {
        acc[rid] = this.Editmodel._merge_to_display(vals, {})
      } else if ([1].includes(op)) {
        const oldVal = acc[rid] || {}
        acc[rid] = this.Editmodel._merge_to_display(oldVal, vals)
      } else if ([0].includes(op)) {
        const vals2 = this.Editmodel._merge_to_display({}, vals)
        acc[rid] = { id: rid, ...vals2 }
      } else {
        //
      }

      return acc
    }, {})

    return Object.values(records2)
  }

  _merge_to_onchange(records = []) {
    // call by Editmodel._merge_to_onchange
    // 标准格式为  tuples
    // 其中一个 tup 为 [ op, id, {vals}]
    // op=4: 表示只读.  vals 中数据格式为只读格式
    // op=2,1,0: 表示编辑.  vals 中数据格式为编辑格式
    // merge 为 onchange 参数格式为 [[op,id, {}]]
    // 1. id不同分别处理
    // 2. op=2: 保留原样
    // 3. op=0: 直接保留原样
    // 2. op=4: 直接保留原样
    // 4. op=1: 与 op=4的数据 merge, 并 取op=1.
    // 6. op=1,0: 需要递归处理
    // 3. op=4: 最后 处理 if op=4, then: [ 4, id, false]
    // console.log('_merge_to_display', records)

    // console.log('_merge_to_onchange 11', records)
    const records2 = records.reduce((acc, tup) => {
      const [op, rid, vals] = tup
      if (op === 5) {
        // acc['command5'] = tup
        acc = {}
      } else if ([4].includes(op)) {
        acc[rid] = [op, rid, vals]
      } else if ([3, 2].includes(op)) {
        acc[rid] = [op, rid, false]
      } else if ([1].includes(op)) {
        const oldVal = (acc[rid] || [null, null, {}])[2]
        const vals2 = this.Editmodel._merge_to_onchange(oldVal, vals)
        // console.log(oldVal, vals, vals2)
        if ('id' in vals2) {
          delete vals2.id
        }
        // console.log(vals2)
        acc[rid] = [op, rid, vals2]
      } else if ([0].includes(op)) {
        const vals2 = this.Editmodel._merge_to_onchange({}, vals)
        acc[rid] = [op, rid, vals2]
      } else {
        //
      }
      return acc
    }, {})

    const records3 = Object.values(records2)

    const records4 = records3.map(item => {
      if (item[0] === 4) {
        return [4, item[1], false]
      } else if (item[0] === 1) {
        if (virtualHelper.is_virtual(item[1])) {
          return [0, item[1], item[2]]
        } else {
          return item
        }
      } else {
        return item
      }
    })

    return records4
  }

  _merge_to_write(records = []) {
    //
    // call by Editmodel._merge_to_write
    //
    const records2 = records.filter(tup => [2, 1, 0].includes(tup[0]))
    return records2.map(tup => {
      const [op, rid, vals] = tup
      if (op === 2) {
        return tup
      } else if (op === 1 || op === 0) {
        const vals2 = this.Editmodel._merge_to_write({}, vals)
        return [op, rid, vals2]
      } else {
        return tup
      }
    })
  }

  upinsert_one(res_id, value) {
    // console.log('upinsert_one1', res_id, value)
    const tuple_get = () => {
      if (!res_id) {
        return [0, virtualHelper.virtual_id(), value]
      } else {
        if (virtualHelper.is_virtual(res_id)) {
          return [0, res_id, value]
        } else {
          return [1, res_id, value]
        }
      }
    }
    const value2 = tuple_get()
    // const [op, rid, vals] = value2

    const values = this._merge_values([...this.datastore.values, value2])

    this.datastore.values = values

    // console.log('upinsert_one1 ok', values, this.datastore, this.records_display)

    return {
      values: this.datastore.values,
      records_display: this.records_display
    }
  }

  remove_one(res_id) {
    const value2 = [2, res_id, false]
    const values = this._merge_values([...this.datastore.values, value2])
    this.datastore.values = values
    return {
      values: this.datastore.values,
      records_display: this.records_display
    }
  }
}

const virtualHelper = {
  _virtual_id: 1,
  virtual_id() {
    this._virtual_id = this._virtual_id + 1
    const vid = this._virtual_id
    return `virtual_${vid}`
  },

  is_virtual(rid) {
    return typeof rid === 'string'
  }
}
